#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define HASH_SIZE 2048
//total meaningful bits: 1 + 2 + 3 + 5 = 11 => 2048 entries

typedef struct {
	unsigned int flag;
	unsigned int addr;
	unsigned int value;
	unsigned int pc;

	unsigned int addrd;
	unsigned int valued;
	unsigned int pcd;
} type_last;


int main(int argc, char** argv) {
	FILE* fst = fopen("compressed.bin", "rb");
	if ( !fst ) {
		fprintf( stderr, "cannot find first-order compressed file \"compressed.bin\"!\n" );
		exit(1);
	}

	type_last* type_hash = (type_last*)calloc(HASH_SIZE, sizeof(type_last));
	memset(type_hash, 0, sizeof(type_last)*HASH_SIZE);
	FILE* fcp = fopen("compr2.bin", "wb");

	unsigned int pcddcount = 0;
	unsigned int addrddcount = 0;
	unsigned int valueddcount = 0;
	unsigned int value0 = 0;
	unsigned int value16 = 0;
	unsigned int valuecount = 0;
	unsigned int onlyflagcount = 0;
	unsigned int onlyflagpair = 0;
	int lastonlyflag = 0;

	unsigned int lastpc = 0;
	while ( !feof ( fst ) ) {
		unsigned int flags = 0;
		unsigned int addr = 0;
		unsigned int value = 0;
		unsigned int pc = 0;
		fread(&flags, sizeof(unsigned int), 1, fst);
		unsigned char regChange = (flags>>16)&1;
		unsigned char memAcc = (flags>>17)&3;
		unsigned char size = (flags>>19)&31; // but size is only 1 | 2 | 4? so 3 bits should be enough
		unsigned char target = (flags>>24)&31;
		unsigned char match = (flags>>30)&1;
		if ( match ) {
			fwrite(&flags, sizeof(unsigned int), 1, fcp);
			lastonlyflag = 0;
			continue;
		}
		//total meaningful bits: 1 + 2 + 3 + 5 = 11 => 2048 entries

		if ( memAcc ) {
			fread(&addr, sizeof(unsigned int), 1, fst);
		}
		if ( regChange ) {
			fread(&value, sizeof(unsigned int), 1, fst);
		}
		fread(&pc, sizeof(unsigned int), 1, fst);


		unsigned int idx = (regChange%2) + ((memAcc%4)<<1) + ((size%5)<<3) + ((target%32)<<6);
		type_last* hashv = &type_hash[idx%HASH_SIZE]; // mod by HASH_SIZE just in case
		int addrd = (int)addr - hashv->addr;
		int valued = (int)value - hashv->value;
		int pcd = (int)pc - hashv->pc;
		int addrdd = addrd-hashv->addrd;
		int valuedd = valued - hashv->valued;
		int pcdd = pcd - hashv->pcd;

/*
		printf( "[%3x] ", idx);
		if ( memAcc )
			printf( "%10x ", addrdd );
		else
			printf( "%10s ", "" );

		if ( regChange )
			printf( "%10x ", valuedd );
		else
			printf( "%10s ", "" );

		printf( "%10x\n", pcdd );
*/
		if ( memAcc && !addrdd ) {
			flags |= (1<<21);
			addrddcount++;
		}
		if ( regChange && !valuedd ) {
			flags |= (1<<22);
			valueddcount++;
		}
		if ( !pcdd ) {
			flags |= (1<<23);
			pcddcount++;
		}
		if ( regChange ) {
			valuecount++;
			if ( value == 0 ) value0++;
			if ( value < (1<<16) ) value16++;
		}
		if ( (!memAcc || !addrdd) && (!regChange || !valuedd) && !pcdd ) {
			onlyflagcount++;
			if (lastonlyflag) {
				onlyflagpair++;
				lastonlyflag = 0;
			} else {
				lastonlyflag = 1;
			}
		} else {
			lastonlyflag = 0;
		}
		
		/*
		fwrite(&flags, sizeof(unsigned int), 1, fcp);
		if ( memAcc && addrdd ) {
			fwrite(&addr, sizeof(unsigned int), 1, fcp);
		}
		if ( regChange && valuedd ) {
			fwrite(&value, sizeof(unsigned int), 1, fcp);
		}
		if ( pcdd ) {
			fwrite(&pc, sizeof(unsigned int), 1, fcp);
		}
		*/
//		printf( "[%3x] %12d %12d %12d < %12x\n", idx, addrd, valued, pcd, pc - lastpc );
		lastpc = pc;
		hashv->addr = addr;
		hashv->value = value;
		hashv->pc = pc;

		hashv->addrd = addrd;
		hashv->valued = valued;
		hashv->pcd = pcd;
	}

	printf( "addrdd:%d pcdd:%d valuedd:%d\n", addrddcount, pcddcount, valueddcount );
	printf( "valuecount:%d value0:%d value16:%d\n", valuecount, value0, value16 );
	printf( "onlyflag:%d onlyflagpair:%d\n", onlyflagcount, onlyflagpair);

	fclose(fst);
	fclose(fcp);
	return 0;
}
